/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Log.print('service module: sinedria');

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria = {};

// Το service "sinedria.nea" καλείται κατά την είσοδο του παίκτη στην εφαρμογή και επιχειρεί
// να εισαγάγει νέα συνεδρία στο σκηνικό. Αυτή η διαδικασία ακολουθεί τα παρακάτω βήματα:

Service.sinedria.nea = function(nodereq) {
	if (nodereq.anonimo()) return;
	if (!nodereq.url.hasOwnProperty('kodikos')) return nodereq.end('Δεν περάστηκε κωδικός');

	// Θα χρησιμοποιήσουμε κάποιο property του αιτήματος ως flag νέων κινήσεων στο transaction
	// log, οπότε αρχικά το θέτουμε false:

	nodereq.url.kinisi = false;

	// Χρονοκαθυστέρηση για λόγους ασφαλείας.

	setTimeout(function() {
		Service.sinedria.nea1(nodereq);
	}, 500);
}

// Πρώτο βήμα στη δημιουργία και εισαγωγή νέας συνεδρίας είναι το κλείδωμα του σχετικού παίκτη.
// Δεν υπάρχει ιδιαίτερος λόγος, αλλά πιθανόν μας διασφαλίζει από κάποιες ανεπιθύμητες καταστάσεις
// που ίσως προκύψουν από ταυτόχρονες ενέργειες εισόδου/εξόδου του ιδίου παίκτη.

Service.sinedria.nea1 = function(nodereq) {
	// Αρχικά δεν εξετάζουμε αν ο παίκτης βρίσκεται ήδη ή όχι στο σκηνικό. Αντ' αυτού
	// δημιουργούμε «εναέριο» παίκτη στον οποίο προσδίδουμε μόνο τα στοιχεία εισόδου.

	var pektis = new Pektis({
		login: nodereq.login,
		kodikos: nodereq.url.kodikos,
	}).

	// Επιχειρούμε να κλειδώσουμε τον παίκτη.

	klidomaPektis(DB.connection(), {
		// Αν ο παίκτης κλειδώθηκε επιτυχώς, προχωρούμε στο επόμενο βήμα.

		onsuccess: function(conn, pektis) {
			Service.sinedria.nea2(nodereq, conn, pektis);
		},

		// Ο παίκτης δεν κλειδώθηκε οπότε ακυρώνουμε τη διαδικασία.

		onerror: function(conn, pektis) {
			conn.free();
			nodereq.end(pektis.login + ': ο παίκτης είναι κλειδωμένος');
		},
	});
}

// Επόμενο βήμα είναι να προσπελάσουμε τον παίκτη με τα στοιχεία που δόθηκαν στη φόρμα
// εισόδου.

Service.sinedria.nea2 = function(nodereq, conn, pektis) {
	var skiniko = nodereq.skinikoGet(), query, sitkep;

	// Αν ο παίκτης υπάρχει ήδη στο σκηνικό, ελέγχουμε απλώς τα στοιχεία εισόδου.

	sitkep = skiniko.pektisGet(pektis.login);
	if (sitkep) {
		query = 'SELECT `login` FROM `pektis` WHERE (`login` = BINARY ' +
			globals.json(pektis.login) + ') AND (`kodikos` = BINARY ' +
			globals.json(pektis.kodikos) + ')';
		conn.query(query, function(conn, rows) {
			// Αν ο παίκτης προσπελάστηκε επιτυχώς, προχωρούμε στην επόμενη φάση
			// με τον παίκτη που ήδη υπάρχει στο σκηνικό.

			if (rows.length) Service.sinedria.nea2ok(nodereq, conn, sitkep);

			// Τα στοιχεία εισόδου δεν είναι ακριβή, επομένως ακυρώνουμε τη διαδικασία
			// αποστέλλοντας σχετικό μήνυμα.

			else Service.sinedria.neaTelos(nodereq, conn, pektis, 'Access denied');
		});

		return;
	}

	// Αν ο παίκτης δεν υπάρχει στο σκηνικό, τότε ελέγχουμε τα στοιχεία εισόδου
	// συμπληρώνοντας ταυτόχρονα όλα τα υπόλοιπα στοιχεία τού παίκτη τον οποίο,
	// εν τέλει, εισάγουμε τώρα στο σκηνικό.

	pektis.pektisLoad(

	// Τα στοιχεία του παίκτη ενημερώθηκαν επιτυχώς από την database, οπότε
	// τον εισάγουμε στο σκηνικό και προχωρούμε στην επόμενη φάση.

	function(pektis) {
		// Δεν εισάγουμε απευθείας τον παίκτη στην κίνηση καθώς θα αποκόψουμε
		// ευαίσθητα δεδομένα, ακόμη και από τον ίδιο τον παίκτη, επομένως
		// προτιμούμε να εισαγάγουμε αντίγραφο του παίκτη.

		skiniko.
		pektisAdd(pektis).
		kinisiAdd(new Kinisi('PK').set({
			p: new Pektis(pektis).pektis2object().pektisIncognito(),
		}), false);
		nodereq.url.kinisi = true;
		Service.sinedria.nea2ok(nodereq, conn, pektis);
	},

	// Τα στοιχεία εισόδου δεν είναι ακριβή, επομένως ακυρώνουμε τη διαδικασία
	// αποστέλλοντας σχετικό μήνυμα.

	function() {
		Service.sinedria.neaTelos(nodereq, conn, pektis, 'Access denied');
	},

	// Περνάμε την τρέχουσα database connection, ώστε να μην δημιουργηθεί νέα
	// database connection από την "pektisLoad".

	conn);
}

// Η function που ακολουθεί καλείται εφόσον τα στοιχεία εισόδου ήσαν ορθά. Αν, λοιπόν,
// ο παίκτης έχει δώσει τα σωστά στοιχεία εισόδου, θα προχωρήσουμε στις επόμενες φάσεις
// αλλά στα πλαίσια ενιαίας transaction, καθώς θα μεσολαβήσει, ενδεχομένως, αρχειοθέτηση
// και διαγραφή τυχόν υπάρχουσας συνεδρίας του παίκτη.

Service.sinedria.nea2ok = function(nodereq, conn, pektis) {
	conn.transaction(function(conn) {
		Service.sinedria.nea3(nodereq, conn, pektis);
	});
}

// Στην επόμενη φάση είμαστε σε φάση ενιαίας database transaction και επιχειρούμε να
// αρχειοθετήσουμε και να διαγράψουμε τυχόν ήδη υπάρχουσα συνεδρία του παίκτη.

Service.sinedria.nea3 = function(nodereq, conn, pektis) {
	pektis.sinedriaArxio(conn,

	// Αν η διαδικασία της αρχειοθέτησης και διαγραφής τυχόν υπάρχουσας συνεδρίας του
	// παίκτη δεν παρουσίασε πρόβλημα, προχωρούμε στην επόμενη φάση.

	function(conn, pektis) {
		Service.sinedria.nea4(nodereq, conn, pektis);
	},

	// Η διαδικασία αρχειοθέτησης και διαγραφής της ήδη υπάρχουσας συνεδρίας του παίκτη
	// παρουσίασε πρόβλημα, οπότε ακυρώνουμε την tranasaction, ξεκλειδώνουμε τον παίκτη
	// και ακυρώνουμε το αίτημα αποστέλλοντας σχετικό μήνυμα.

	function(conn, pektis) {
		conn.rollback(function(conn) {
			Service.sinedria.neaTelos(nodereq, conn, pektis, 'Απέτυχε αρχειοθέτηση προηγούμενης συνεδρίας');
		});
	});
}

// Στην επόμενη φάση, δημιουργούμε τη νέα συνεδρία και επιχειρούμε να την εισαγάγουμε στην
// database.

Service.sinedria.nea4 = function(nodereq, conn, pektis) {
	var
	skiniko = nodereq.skinikoGet(),
	sinedria = new Sinedria({
		pektis: pektis.login,
		klidi: nodereq.klidi,
		ip: nodereq.ip,
		isodos: globals.tora(),
		poll: globals.tora(),
	}),
	query = 'INSERT INTO `sinedria` (`pektis`, `klidi`, `ip`) VALUES (' +
		globals.json(sinedria.pektis) + ', ' + globals.json(sinedria.klidi) + ', ' +
		globals.json(sinedria.ip) + ')';

	conn.query(query, function(conn) {
		// Αν η εισαγωγή της συνεδρίας στην database έχει αποτύχει, ακυρώνουμε την
		// transaction, ξεκλειδώνουμε τον παίκτη και ακυρώνουμε το αίτημα αποστέλλοντας
		// σχετικό μήνυμα.

		if (conn.affectedRows != 1) conn.rollback(function(conn) {
			Service.sinedria.neaTelos(nodereq, conn, pektis, 'Απέτυχε η δημιουργία νέας συνεδρίας');
		});

		// Η συνεδρία εισήχθη επιτυχώς στην database, συνεπώς κλείνουμε την transaction,
		// εντοπίζουμε και εισάγουμε τη συνεδρία στο σκηνικό, ενημερώνουμε τους clients
		// και κλείνουμε το αίτημα και την όλη διαδικασία.

		else conn.commit(function(conn) {
			skiniko.sinedriaAdd(sinedria.entopismos(skiniko)).

			// Στην κίνηση νέας συνεδρίας αποστέλλουμε αντίγραφο της συνεδρίας
			// καθώς θα αποκόψουμε ευαίσθητα προσωπικά δεδομένα.

			kinisiAdd(new Kinisi('SN').set({
				p: sinedria.pektisGet(),
			}), false);
			nodereq.url.kinisi = true;
			Service.sinedria.neaTelos(nodereq, conn, pektis);
		});
	});
}

// Η function που ακολουθεί καλείται στο τέλος της διαδικασίας με σκοπό το ξεκλείδωμα
// του παίκτη και το κλείσιμο του αιτήματος. Αν δοθεί μήνυμα λάθους, αυτό αποστέλλεται
// στον αιτούντα client και σηματοδοτεί την αποτυχία της όλης διαδικασίας.

Service.sinedria.neaTelos = function(nodereq, conn, pektis, errmsg) {
	var skiniko = nodereq.skinikoGet();

	pektis.xeklidomaPektis(conn, function(conn) {
		conn.free();
		nodereq.end(errmsg);

		// Αν έχουν εισαχθεί κινήσεις στο transaction log από την ως άνω
		// διαδικασία, τώρα είναι η ώρα να ενημερωθούν οι clients για τις
		// μεταβολές, ακόμη και αν η διαδικασία έχει κλείσει με σφάλματα.

		if (nodereq.url.kinisi) skiniko.enimerosi();
	});
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Θα υπολογιστεί κατά το στήσιμο των περιπόλων.

Service.sinedria.timeout = 0;

Service.sinedria.check = function(skiniko) {
	var tora = globals.tora();

	skiniko.sinedriaWalk(function(login, sinedria) {
		if (tora - sinedria.pollGet() < Service.sinedria.timeout) return;

		console.log('sinedria timeout: ' + login);
		Service.sinedria.lixi1(skiniko, login);
	});
}

Service.sinedria.lixi1 = function(skiniko, login) {
	var pektis = new Pektis({
		login: login,
	}).klidomaPektis(DB.connection(), {
		onsuccess: function(conn, pektis) {
			conn.transaction(function(conn) {
				Service.sinedria.lixi2(conn, skiniko, pektis, login);
			});
		},

		onerror: function(conn) {
			conn.free();
			console.error(login + ': ο παίκτης είναι κλειδωμένος (sinedria timeout)');
		},
	});
}

Service.sinedria.lixi2 = function(conn, skiniko, pektis, login) {
	pektis.sinedriaArxio(conn, function(conn, pektis) {
		conn.commit(function(conn) {
			skiniko.
			feredataClose(login).

			rebelosDelete(login).
			sinedriaDelete(login);

			pektis.xeklidomaPektis(conn, function(conn) {
				conn.free();
				skiniko.kinisiAdd(new Kinisi('NS').set({
					p: login,
				}));
			});
		});
	}, function(conn, pektis) {
		conn.rollback(function(conn) {
			pektis.xeklidomaPektis(conn, function(conn) {
				conn.free();
				console.error(login + ': απέτυχε η αρχειοθέτηση της συνεδρίας (sinedria timeout)');
			});
		});
	});
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.exodos = function(nodereq) {
	if (nodereq.isvoli()) return;

	setTimeout(function() {
		Service.sinedria.exodos1(nodereq);
	}, 500);
}

Service.sinedria.exodos1 = function(nodereq) {
	var pektis = new Pektis({
		login: nodereq.login,
	}).klidomaPektis(DB.connection(), {
		onsuccess: function(conn, pektis) {
			conn.transaction(function(conn) {
				Service.sinedria.exodos2(nodereq, conn, pektis);
			});
		},

		onerror: function(conn, pektis) {
			conn.free();
			nodereq.end(pektis.login + ': ο παίκτης είναι κλειδωμένος');
		},
	});
}

Service.sinedria.exodos2 = function(nodereq, conn, pektis) {
	var skiniko = nodereq.skinikoGet();

	pektis.sinedriaArxio(conn, function(conn, pektis) {
		conn.commit(function(conn) {
			skiniko.
			feredataClose(pektis.login).

			rebelosDelete(pektis.login).
			sinedriaDelete(pektis.login);

			pektis.xeklidomaPektis(conn, function(conn) {
				conn.free();
				nodereq.end();
				skiniko.kinisiAdd(new Kinisi('NS').set({
					p: pektis.login,
				}));
			});
		});
	}, function(conn, pektis) {
		conn.rollback(function(conn) {
			pektis.xeklidomaPektis(conn, function(conn) {
				conn.free();
				nodereq.end('Απέτυχε η αρχειοθέτηση της συνεδρίας');
			});
		});
	});
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.pektisTheatis = function(nodereq) {
	var skiniko = nodereq.skinikoGet(), sinedria;

	if (nodereq.isvoli()) return;
	sinedria = nodereq.sinedriaGet();
	if (sinedria.isRebelos()) return nodereq.end('Ακαθόριστο τραπέζι κατά την αλλαγή ρόλου');
	if (sinedria.isTheatis() && nodereq.trapeziGet().pektisGet(sinedria.thesiGet()))
		return nodereq.end('Η θέση είναι κατειλημμένη');

	nodereq.end();
	sinedria.pektisTheatis();
	skiniko.kinisiAdd(new Kinisi('PT').set({
		p: sinedria.pektisGet(),
	}));
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.thesiRoloi = function(nodereq) {
	var skiniko = nodereq.skinikoGet(), trapezi, sinedria, pektis, thesi, theatis;

	if (nodereq.isvoli()) return;
	trapezi = nodereq.trapeziGet();
	if (!trapezi) return nodereq.end('Ακαθόριστο τραπέζι κατά την αλλαγή θέσης');

	nodereq.end();

	pektis = nodereq.loginGet();
	sinedria = nodereq.sinedriaGet();
	thesi = sinedria.thesiGet();

	if (sinedria.isTheatis()) {
		if (++thesi > geister.thesiMax) thesi = 1;
		theatis = trapezi.theatisGet(pektis);
		theatis.thesiSet(thesi);
		sinedria.thesiSet(thesi);
	}
	else sinedria.thesiRoloi();

	skiniko.kinisiAdd(new Kinisi('AH').set({
		p: pektis,
	}));
}
